package eval

import (
	"showen/lex"
	"showen/num"
	"showen/obj"
	"showen/parse"
	"testing"
)

/*
	func TestList(t *testing.T) {
		env := obj.NewEnvironment()
		RegisterBuiltinProcedures(env)

		// Test empty list
		args := []obj.Object{}
		result := List(args, env)
		if result.Type() != obj.NULL_OBJ {
			t.Errorf("Expected empty list to return null object, got %v", result.Type())
		}

		// Test list with one element
		args = []obj.Object{
			&obj.NumberObject{Value: num.Integer{Value: 1}},
		}
		result = List(args, env)
		if result.Type() != obj.PAIR_OBJ {
			t.Errorf("Expected list to return pair object, got %v", result.Type())
		}

		// Verify the list structure
		pair := result.(*obj.PairObject)
		if ds.Car(pair) != (num.Integer{Value: 1}) {
			t.Errorf("Expected first element to be 1, got %v", ds.Car(pair))
		}
		if ds.Cdr(pair) != nil {
			t.Errorf("Expected cdr to be nil, got %v", ds.Cdr(pair))
		}

		// Test list with multiple elements
		args = []obj.Object{
			&obj.NumberObject{Value: num.Integer{Value: 1}},
			&obj.NumberObject{Value: num.Integer{Value: 2}},
			&obj.NumberObject{Value: num.Integer{Value: 3}},
		}
		result = List(args, env)

		// Verify the list structure
		pair = result.(*obj.PairObject).Value
		if ds.Car(pair) != (num.Integer{Value: 1}) {
			t.Errorf("Expected first element to be 1, got %v", ds.Car(pair))
		}

		second := ds.Cdr(pair)
		if second == nil || !ds.IsPair(second) {
			t.Errorf("Expected second element to be a pair, got %v", second)
		}

		secondPair := second.(*ds.Pair)
		if ds.Car(secondPair) != (num.Integer{Value: 2}) {
			t.Errorf("Expected second element to be 2, got %v", ds.Car(secondPair))
		}

		third := ds.Cdr(secondPair)
		if third == nil || !ds.IsPair(third) {
			t.Errorf("Expected third element to be a pair, got %v", third)
		}

		thirdPair := third.(*ds.Pair)
		if ds.Car(thirdPair) != (num.Integer{Value: 3}) {
			t.Errorf("Expected third element to be 3, got %v", ds.Car(thirdPair))
		}

		if ds.Cdr(thirdPair) != nil {
			t.Errorf("Expected end of list to be nil, got %v", ds.Cdr(thirdPair))
		}
	}
*/
/*
func TestIsPairProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with actual pair
	pair := &obj.PairObject{Value: ds.NewPair(1, 2)}
	args := []obj.Object{pair}
	result := IsPairProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected pair to be identified as pair")
	}

	// Test with non-pair
	number := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{number}
	result = IsPairProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected number not to be identified as pair")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsPairProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}
*/
func TestAdd(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with no arguments
	result := Add([]obj.Object{}, env)
	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Add with no arguments should return INTEGER_OBJ, got %v", result.Type())
	}

	intResult := result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 0 {
		t.Errorf("Add with no arguments should return 0, got %v", intResult.Value)
	}

	// Test with one argument
	result = Add([]obj.Object{&obj.NumberObject{Value: num.Integer{Value: 5}}}, env)
	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Add should return INTEGER_OBJ, got %v", result.Type())
	}

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 5 {
		t.Errorf("Add with one argument should return that argument, got %v", intResult.Value)
	}

	// Test with multiple arguments
	result = Add([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 1}},
		&obj.NumberObject{Value: num.Integer{Value: 2}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
	}, env)

	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Add should return INTEGER_OBJ, got %v", result.Type())
	}

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 6 {
		t.Errorf("Add with 1, 2, 3 should return 6, got %v", intResult.Value)
	}

	// Test with negative numbers
	result = Add([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 10}},
		&obj.NumberObject{Value: num.Integer{Value: -3}},
	}, env)

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 7 {
		t.Errorf("Add with 10, -3 should return 7, got %v", intResult.Value)
	}

	// Test with non-integer argument
	result = Add([]obj.Object{&obj.BooleanObject{Value: true}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Add with non-integer should return ERROR_OBJ, got %v", result.Type())
	}
}

func TestIsBooleanProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with actual boolean (#t)
	booleanTrue := &obj.BooleanObject{Value: true}
	args := []obj.Object{booleanTrue}
	result := IsBooleanProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected #t to be identified as boolean")
	}

	// Test with actual boolean (#f)
	booleanFalse := &obj.BooleanObject{Value: false}
	args = []obj.Object{booleanFalse}
	result = IsBooleanProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected #f to be identified as boolean")
	}

	// Test with non-boolean
	number := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{number}
	result = IsBooleanProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected number not to be identified as boolean")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsBooleanProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}

	args = []obj.Object{booleanTrue, booleanTrue}
	result = IsBooleanProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsSymbolProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with actual symbol
	symbol := &obj.SymbolObject{Value: "test"}
	args := []obj.Object{symbol}
	result := IsSymbolProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected symbol to be identified as symbol")
	}

	// Test with non-symbol
	number := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{number}
	result = IsSymbolProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected number not to be identified as symbol")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsSymbolProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsNumberProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with actual number
	number := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args := []obj.Object{number}
	result := IsNumberProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected number to be identified as number")
	}

	// Test with non-number
	booleanVal := &obj.BooleanObject{Value: true}
	args = []obj.Object{booleanVal}
	result = IsNumberProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected boolean not to be identified as number")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsNumberProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsIntegerProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with actual integer
	integer := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args := []obj.Object{integer}
	result := IsIntegerProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected integer to be identified as integer")
	}

	// Test with actual real
	real := &obj.NumberObject{Value: num.Real{Value: 1.0}}
	args = []obj.Object{real}
	result = IsIntegerProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected real not to be identified as integer")
	}

	// Test with actual complex
	complex := &obj.NumberObject{Value: num.Complex{RealPart: num.Integer{Value: 1}, ImagPart: num.Integer{Value: 2}}}
	args = []obj.Object{complex}
	result = IsIntegerProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected complex not to be identified as integer")
	}

	// Test with non-number
	booleanVal := &obj.BooleanObject{Value: true}
	args = []obj.Object{booleanVal}
	result = IsIntegerProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected boolean not to be identified as integer")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsIntegerProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsRationalProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with rational
	rational := &obj.NumberObject{Value: num.Rational{Numerator: 1, Denominator: 2}}
	args := []obj.Object{rational}
	result := IsRationalProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected rational to be identified as rational")
	}

	// Test with integer
	integer := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{integer}
	result = IsRationalProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected integer to be identified as rational")
	}

	// Test with non-number
	booleanVal := &obj.BooleanObject{Value: true}
	args = []obj.Object{booleanVal}
	result = IsRationalProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected boolean not to be identified as rational")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsRationalProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsRealProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with real
	realNum := &obj.NumberObject{Value: num.Real{Value: 1.5}}
	args := []obj.Object{realNum}
	result := IsRealProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected real to be identified as real")
	}

	// Test with integer
	integer := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{integer}
	result = IsRealProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected integer to be identified as real")
	}

	// Test with non-number
	booleanVal := &obj.BooleanObject{Value: true}
	args = []obj.Object{booleanVal}
	result = IsRealProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected boolean not to be identified as real")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsRealProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsComplexProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with complex
	complexNum := &obj.NumberObject{Value: num.Complex{RealPart: num.Integer{Value: 1}, ImagPart: num.Integer{Value: 2}}}
	args := []obj.Object{complexNum}
	result := IsComplexProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected complex to be identified as complex")
	}

	// Test with integer
	integer := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{integer}
	result = IsComplexProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected integer to be identified as complex")
	}

	// Test with non-number
	booleanVal := &obj.BooleanObject{Value: true}
	args = []obj.Object{booleanVal}
	result = IsComplexProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected boolean not to be identified as complex")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsComplexProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsCharProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with actual character
	character := &obj.CharacterObject{Value: 'a'}
	args := []obj.Object{character}
	result := IsCharProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected character to be identified as character")
	}

	// Test with non-character
	number := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{number}
	result = IsCharProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected number not to be identified as character")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsCharProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsStringProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with actual string
	str := &obj.StringObject{Value: "test"}
	args := []obj.Object{str}
	result := IsStringProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected string to be identified as string")
	}

	// Test with non-string
	number := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{number}
	result = IsStringProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected number not to be identified as string")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsStringProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestIsProcedureProc(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with builtin procedure
	builtin := &obj.Builtin{}
	args := []obj.Object{builtin}
	result := IsProcedureProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected builtin to be identified as procedure")
	}

	// Test with lambda
	lambda := &obj.Lambda{}
	args = []obj.Object{lambda}
	result = IsProcedureProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected lambda to be identified as procedure")
	}

	// Test with user-defined procedure
	proc := &obj.Procedure{}
	args = []obj.Object{proc}
	result = IsProcedureProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected procedure to be identified as procedure")
	}

	// Test with non-procedure
	number := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{number}
	result = IsProcedureProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected number not to be identified as procedure")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsProcedureProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestLessThan(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test with builtin procedure
	builtin := &obj.Builtin{}
	args := []obj.Object{builtin}
	result := IsProcedureProc(args, env)

	booleanResult, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected builtin to be identified as procedure")
	}

	// Test with lambda
	lambda := &obj.Lambda{}
	args = []obj.Object{lambda}
	result = IsProcedureProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected lambda to be identified as procedure")
	}

	// Test with user-defined procedure
	proc := &obj.Procedure{}
	args = []obj.Object{proc}
	result = IsProcedureProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if !booleanResult.Value {
		t.Errorf("Expected procedure to be identified as procedure")
	}

	// Test with non-procedure
	number := &obj.NumberObject{Value: num.Integer{Value: 1}}
	args = []obj.Object{number}
	result = IsProcedureProc(args, env)

	booleanResult, ok = result.(*obj.BooleanObject)
	if !ok {
		t.Errorf("Expected boolean result, got %v", result.Type())
	}

	if booleanResult.Value {
		t.Errorf("Expected number not to be identified as procedure")
	}

	// Test with wrong number of arguments
	args = []obj.Object{}
	result = IsProcedureProc(args, env)

	_, ok = result.(*Error)
	if !ok {
		t.Errorf("Expected error with wrong number of arguments, got %v", result.Type())
	}
}

func TestSubtract(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with no arguments
	result := Subtract([]obj.Object{}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Subtract with no arguments should return ERROR_OBJ, got %v", result.Type())
	}

	// Test with one argument
	result = Subtract([]obj.Object{&obj.NumberObject{Value: num.Integer{Value: 5}}}, env)
	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Subtract should return INTEGER_OBJ, got %v", result.Type())
	}

	intResult := result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != -5 {
		t.Errorf("Subtract with one argument should negate it, got %v", intResult.Value)
	}

	// Test with multiple arguments
	result = Subtract([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 10}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
		&obj.NumberObject{Value: num.Integer{Value: 2}},
	}, env)

	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Subtract should return INTEGER_OBJ, got %v", result.Type())
	}

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 5 {
		t.Errorf("Subtract 10 - 3 - 2 should return 5, got %v", intResult.Value)
	}

	// Test with negative numbers
	result = Subtract([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 10}},
		&obj.NumberObject{Value: num.Integer{Value: -3}},
	}, env)

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 13 {
		t.Errorf("Subtract 10 - (-3) should return 13, got %v", intResult.Value)
	}

	// Test with non-integer argument
	result = Subtract([]obj.Object{&obj.BooleanObject{Value: true}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Subtract with non-integer should return ERROR_OBJ, got %v", result.Type())
	}
}

func TestMultiply(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with no arguments
	result := Multiply([]obj.Object{}, env)
	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Multiply with no arguments should return INTEGER_OBJ, got %v", result.Type())
	}

	intResult := result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 1 {
		t.Errorf("Multiply with no arguments should return 1, got %v", intResult.Value)
	}

	// Test with one argument
	result = Multiply([]obj.Object{&obj.NumberObject{Value: num.Integer{Value: 5}}}, env)
	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Multiply should return INTEGER_OBJ, got %v", result.Type())
	}

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 5 {
		t.Errorf("Multiply with one argument should return that argument, got %v", intResult.Value)
	}

	// Test with multiple arguments
	result = Multiply([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 2}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
		&obj.NumberObject{Value: num.Integer{Value: 4}},
	}, env)

	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Multiply should return INTEGER_OBJ, got %v", result.Type())
	}

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 24 {
		t.Errorf("Multiply with 2, 3, 4 should return 24, got %v", intResult.Value)
	}

	// Test with zero
	result = Multiply([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 7}},
		&obj.NumberObject{Value: num.Integer{Value: 0}},
	}, env)

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 0 {
		t.Errorf("Multiply with 7, 0 should return 0, got %v", intResult.Value)
	}

	// Test with negative numbers
	result = Multiply([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: -2}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
	}, env)

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != -6 {
		t.Errorf("Multiply with -2, 3 should return -6, got %v", intResult.Value)
	}

	// Test with non-integer argument
	result = Multiply([]obj.Object{&obj.BooleanObject{Value: true}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Multiply with non-integer should return ERROR_OBJ, got %v", result.Type())
	}
}

func TestDivide(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with no arguments
	result := Divide([]obj.Object{}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Divide with no arguments should return ERROR_OBJ, got %v", result.Type())
	}

	// Test with one argument (reciprocal)
	result = Divide([]obj.Object{&obj.NumberObject{Value: num.Integer{Value: 5}}}, env)
	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Divide should return NUMBER_OBJ, got %v", result.Type())
	}

	// 1/5 should be 1/5 as a rational
	rationalResult := result.(*obj.NumberObject)
	if rationalResult.Value.Type() != num.RATIONAL_TYPE {
		t.Errorf("Divide with one argument should return RATIONAL_TYPE, got %v", rationalResult.Value.Type())
	}

	rat := rationalResult.Value.(num.Rational)
	if rat.Numerator != 1 || rat.Denominator != 5 {
		t.Errorf("Divide with 5 should return 1/5, got %v/%v", rat.Numerator, rat.Denominator)
	}

	// Test with multiple arguments
	result = Divide([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 24}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
		&obj.NumberObject{Value: num.Integer{Value: 2}},
	}, env)

	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Divide should return NUMBER_OBJ, got %v", result.Type())
	}

	intResult := result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != 4 {
		t.Errorf("Divide 24 / 3 / 2 should return 4, got %v", intResult.Value)
	}

	// Test with rational result
	result = Divide([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 7}},
		&obj.NumberObject{Value: num.Integer{Value: 2}},
	}, env)

	if result.Type() != obj.NUMBER_OBJ {
		t.Errorf("Divide should return NUMBER_OBJ, got %v", result.Type())
	}

	rationalResult = result.(*obj.NumberObject)
	if rationalResult.Value.Type() != num.RATIONAL_TYPE {
		t.Errorf("Divide 7 / 2 should return RATIONAL_TYPE, got %v", rationalResult.Value.Type())
	}

	rat = rationalResult.Value.(num.Rational)
	if rat.Numerator != 7 || rat.Denominator != 2 {
		t.Errorf("Divide 7 / 2 should return 7/2, got %v/%v", rat.Numerator, rat.Denominator)
	}

	// Test with negative numbers
	result = Divide([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: -10}},
		&obj.NumberObject{Value: num.Integer{Value: 2}},
	}, env)

	intResult = result.(*obj.NumberObject)
	if intResult.Value.(num.Integer).Value != -5 {
		t.Errorf("Divide -10 / 2 should return -5, got %v", intResult.Value)
	}

	// Test with non-integer argument
	result = Divide([]obj.Object{&obj.BooleanObject{Value: true}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Divide with non-integer should return ERROR_OBJ, got %v", result.Type())
	}
}

func TestRegisterBuiltinProcedures(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Check that +, -, * are registered
	addProc, ok := env.Get("+")
	if !ok {
		t.Error("Add procedure (+) should be registered")
	}
	if addProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Add procedure should be BUILTIN_OBJ, got %v", addProc.Type())
	}

	subProc, ok := env.Get("-")
	if !ok {
		t.Error("Subtract procedure (-) should be registered")
	}
	if subProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Subtract procedure should be BUILTIN_OBJ, got %v", subProc.Type())
	}

	mulProc, ok := env.Get("*")
	if !ok {
		t.Error("Multiply procedure (*) should be registered")
	}
	if mulProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Multiply procedure should be BUILTIN_OBJ, got %v", mulProc.Type())
	}

	divProc, ok := env.Get("/")
	if !ok {
		t.Error("Divide procedure (/) should be registered")
	}
	if divProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Divide procedure should be BUILTIN_OBJ, got %v", divProc.Type())
	}

	// Check comparison procedures
	gtProc, ok := env.Get(">")
	if !ok {
		t.Error("Greater than procedure (>) should be registered")
	}
	if gtProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Greater than procedure should be BUILTIN_OBJ, got %v", gtProc.Type())
	}

	ltProc, ok := env.Get("<")
	if !ok {
		t.Error("Less than procedure (<) should be registered")
	}
	if ltProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Less than procedure should be BUILTIN_OBJ, got %v", ltProc.Type())
	}

	eqProc, ok := env.Get("=")
	if !ok {
		t.Error("Equal procedure (=) should be registered")
	}
	if eqProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Equal procedure should be BUILTIN_OBJ, got %v", eqProc.Type())
	}

	gteProc, ok := env.Get(">=")
	if !ok {
		t.Error("Greater than or equal procedure (>=) should be registered")
	}
	if gteProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Greater than or equal procedure should be BUILTIN_OBJ, got %v", gteProc.Type())
	}

	lteProc, ok := env.Get("<=")
	if !ok {
		t.Error("Less than or equal procedure (<=) should be registered")
	}
	if lteProc.Type() != obj.BUILTIN_OBJ {
		t.Errorf("Less than or equal procedure should be BUILTIN_OBJ, got %v", lteProc.Type())
	}
}

func TestComparisonFunctions(t *testing.T) {
	env := obj.NewEnvironment()

	// Test greaterThan
	result := greaterThan([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 5}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
	}, env)

	boolResult := result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("5 > 3 should be true")
	}
	result = greaterThan([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 3}},
		&obj.NumberObject{Value: num.Integer{Value: 5}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("3 > 5 should be false")
	}

	// Test lessThan
	result = lessThan([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 3}},
		&obj.NumberObject{Value: num.Integer{Value: 5}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("3 < 5 should be true")
	}

	result = lessThan([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 5}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("5 < 3 should be false")
	}

	// Test equal
	result = equal([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 5}},
		&obj.NumberObject{Value: num.Integer{Value: 5}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("5 = 5 should be true")
	}

	result = equal([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 5}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("5 = 3 should be false")
	}

	// Test greaterThanOrEqual
	result = greaterThanOrEqual([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 5}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("5 >= 3 should be true")
	}

	result = greaterThanOrEqual([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 5}},
		&obj.NumberObject{Value: num.Integer{Value: 5}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("5 >= 5 should be true")
	}

	result = greaterThanOrEqual([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 3}},
		&obj.NumberObject{Value: num.Integer{Value: 5}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("3 >= 5 should be false")
	}

	// Test lessThanOrEqual
	result = lessThanOrEqual([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 3}},
		&obj.NumberObject{Value: num.Integer{Value: 5}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("3 <= 5 should be true")
	}

	result = lessThanOrEqual([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 5}},
		&obj.NumberObject{Value: num.Integer{Value: 5}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("5 <= 5 should be true")
	}

	result = lessThanOrEqual([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 5}},
		&obj.NumberObject{Value: num.Integer{Value: 3}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("5 <= 3 should be false")
	}
}

func TestComparisonFunctionsWithNonNumbers(t *testing.T) {
	env := obj.NewEnvironment()

	// Test greaterThan with non-number argument
	result := greaterThan([]obj.Object{
		&obj.BooleanObject{Value: true},
		&obj.NumberObject{Value: num.Integer{Value: 5}},
	}, env)

	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("greaterThan with non-number argument should return ERROR_OBJ, got %v", result.Type())
	}

	// Test lessThan with non-number argument
	result = lessThan([]obj.Object{
		&obj.NumberObject{Value: num.Integer{Value: 3}},
		&obj.BooleanObject{Value: true},
	}, env)

	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("lessThan with non-number argument should return ERROR_OBJ, got %v", result.Type())
	}
}

func TestComparisonFunctionsWithComplexNumbers(t *testing.T) {
	env := obj.NewEnvironment()

	// Test equal with complex numbers that are equal
	result := equal([]obj.Object{
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
	}, env)

	if result.Type() != obj.BOOLEAN_OBJ {
		t.Errorf("equal with complex numbers should return obj.BOOLEAN_OBJ, got %v", result.Type())
	}

	boolResult := result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("Complex numbers (3+4i) and (3+4i) should be equal")
	}

	// Test equal with complex numbers that are not equal (different real parts)
	result = equal([]obj.Object{
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 5},
			ImagPart: num.Integer{Value: 4},
		}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("Complex numbers (3+4i) and (5+4i) should not be equal")
	}

	// Test equal with complex numbers that are not equal (different imaginary parts)
	result = equal([]obj.Object{
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 5},
		}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("Complex numbers (3+4i) and (3+5i) should not be equal")
	}

	// Test equal with complex numbers that are not equal (both parts different)
	result = equal([]obj.Object{
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 5},
			ImagPart: num.Integer{Value: 6},
		}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("Complex numbers (3+4i) and (5+6i) should not be equal")
	}

	// Test equal with multiple complex numbers that are all equal
	result = equal([]obj.Object{
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if !boolResult.Value {
		t.Errorf("Three identical complex numbers should be equal")
	}

	// Test equal with multiple complex numbers where one is different
	result = equal([]obj.Object{
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 3},
			ImagPart: num.Integer{Value: 4},
		}},
		&obj.NumberObject{Value: num.Complex{
			RealPart: num.Integer{Value: 5},
			ImagPart: num.Integer{Value: 6},
		}},
	}, env)

	boolResult = result.(*obj.BooleanObject)
	if boolResult.Value {
		t.Errorf("Three complex numbers where the last one is different should not be equal")
	}
}

/*
	func TestCons(t *testing.T) {
		env := obj.NewEnvironment()
		RegisterBuiltinProcedures(env)

		// Test cons with two numbers
		args := []obj.Object{
			&obj.NumberObject{Value: num.Integer{Value: 1}},
			&obj.NumberObject{Value: num.Integer{Value: 2}},
		}

		result := Cons(args, env)
		if result.Type() != obj.PAIR_OBJ {
			t.Errorf("Expected result type PAIR_OBJ, got %v", result.Type())
		}

		pairObj := result.(*obj.PairObject)

		// Compare integer values
		if carInt, ok := pairObj.Value.Car.(num.Integer); !ok || carInt.Value != 1 {
			t.Errorf("Expected car to be 1, got %v", pairObj.Value.Car)
		}

		if cdrInt, ok := pairObj.Value.Cdr.(num.Integer); !ok || cdrInt.Value != 2 {
			t.Errorf("Expected cdr to be 2, got %v", pairObj.Value.Cdr)
		}

		// Test cons with wrong number of arguments
		args = []obj.Object{
			&obj.NumberObject{Value: num.Integer{Value: 1}},
		}

		result = Cons(args, env)
		if result.Type() != obj.ERROR_OBJ {
			t.Errorf("Expected error with wrong number of arguments")
		}
	}

	func TestCar(t *testing.T) {
		env := obj.NewEnvironment()
		RegisterBuiltinProcedures(env)

		// Create a pair
		pair := ds.NewPair(num.Integer{Value: 1}, num.Integer{Value: 2})
		pairObj := &obj.PairObject{Value: pair}

		// Test car function
		args := []obj.Object{pairObj}
		result := Car(args, env)

		if result.Type() != obj.NUMBER_OBJ {
			t.Errorf("Expected result type NUMBER_OBJ, got %v", result.Type())
		}

		numObj := result.(*obj.NumberObject)
		if intObj, ok := numObj.Value.(num.Integer); !ok || intObj.Value != 1 {
			t.Errorf("Expected car to be 1, got %v", numObj.Value)
		}

		// Test car with wrong number of arguments
		args = []obj.Object{}
		result = Car(args, env)
		if result.Type() != obj.ERROR_OBJ {
			t.Errorf("Expected error with wrong number of arguments")
		}

		// Test car with non-pair argument
		args = []obj.Object{&obj.NumberObject{Value: num.Integer{Value: 1}}}
		result = Car(args, env)
		if result.Type() != obj.ERROR_OBJ {
			t.Errorf("Expected error with non-pair argument")
		}
	}

	func TestCdr(t *testing.T) {
		env := obj.NewEnvironment()
		RegisterBuiltinProcedures(env)

		// Create a pair
		pair := ds.NewPair(num.Integer{Value: 1}, num.Integer{Value: 2})
		pairObj := &obj.PairObject{Value: pair}

		// Test cdr function
		args := []obj.Object{pairObj}
		result := Cdr(args, env)

		if result.Type() != obj.NUMBER_OBJ {
			t.Errorf("Expected result type NUMBER_OBJ, got %v", result.Type())
		}

		numObj := result.(*obj.NumberObject)
		if intObj, ok := numObj.Value.(num.Integer); !ok || intObj.Value != 2 {
			t.Errorf("Expected cdr to be 2, got %v", numObj.Value)
		}

		// Test cdr with wrong number of arguments
		args = []obj.Object{}
		result = Cdr(args, env)
		if result.Type() != obj.ERROR_OBJ {
			t.Errorf("Expected error with wrong number of arguments")
		}

		// Test cdr with non-pair argument
		args = []obj.Object{&obj.NumberObject{Value: num.Integer{Value: 1}}}
		result = Cdr(args, env)
		if result.Type() != obj.ERROR_OBJ {
			t.Errorf("Expected error with non-pair argument")
		}
	}
*/
func TestCharacterComparison(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected bool
	}{
		// char=? tests
		{"char=? with same chars", "(char=? #\\a #\\a)", true},
		{"char=? with different chars", "(char=? #\\a #\\b)", false},
		{"char=? with uppercase and lowercase", "(char=? #\\A #\\a)", false},

		// char<? tests
		{"char<? with smaller char", "(char<? #\\a #\\b)", true},
		{"char<? with larger char", "(char<? #\\b #\\a)", false},
		{"char<? with same chars", "(char<? #\\a #\\a)", false},

		// char>? tests
		{"char>? with larger char", "(char>? #\\b #\\a)", true},
		{"char>? with smaller char", "(char>? #\\a #\\b)", false},
		{"char>? with same chars", "(char>? #\\a #\\a)", false},

		// char<=? tests
		{"char<=? with smaller char", "(char<=? #\\a #\\b)", true},
		{"char<=? with same chars", "(char<=? #\\a #\\a)", true},
		{"char<=? with larger char", "(char<=? #\\b #\\a)", false},

		// char>=? tests
		{"char>=? with larger char", "(char>=? #\\b #\\a)", true},
		{"char>=? with same chars", "(char>=? #\\a #\\a)", true},
		{"char>=? with smaller char", "(char>=? #\\a #\\b)", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Type() != obj.BOOLEAN_OBJ {
				t.Fatalf("Expected BOOLEAN_OBJ, got %v", result.Type())
			}

			boolResult := result.(*obj.BooleanObject)
			if boolResult.Value != tt.expected {
				t.Errorf("Expected %v, got %v", tt.expected, boolResult.Value)
			}
		})
	}
}

func TestCharacterCiComparison(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected bool
	}{
		// char-ci=? tests
		{"char-ci=? with same chars", "(char-ci=? #\\a #\\a)", true},
		{"char-ci=? with different chars", "(char-ci=? #\\a #\\b)", false},
		{"char-ci=? with uppercase and lowercase", "(char-ci=? #\\A #\\a)", true},

		// char-ci<? tests
		{"char-ci<? with smaller char", "(char-ci<? #\\a #\\b)", true},
		{"char-ci<? with uppercase and lowercase", "(char-ci<? #\\A #\\b)", true},
		{"char-ci<? with same chars", "(char-ci<? #\\a #\\A)", false},

		// char-ci>? tests
		{"char-ci>? with larger char", "(char-ci>? #\\b #\\a)", true},
		{"char-ci>? with uppercase and lowercase", "(char-ci>? #\\b #\\A)", true},
		{"char-ci>? with same chars", "(char-ci>? #\\a #\\A)", false},

		// char-ci<=? tests
		{"char-ci<=? with smaller char", "(char-ci<=? #\\a #\\b)", true},
		{"char-ci<=? with same chars", "(char-ci<=? #\\a #\\A)", true},
		{"char-ci<=? with larger char", "(char-ci<=? #\\b #\\a)", false},

		// char-ci>=? tests
		{"char-ci>=? with larger char", "(char-ci>=? #\\b #\\a)", true},
		{"char-ci>=? with same chars", "(char-ci>=? #\\a #\\A)", true},
		{"char-ci>=? with smaller char", "(char-ci>=? #\\a #\\b)", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Type() != obj.BOOLEAN_OBJ {
				t.Fatalf("Expected BOOLEAN_OBJ, got %v", result.Type())
			}

			boolResult := result.(*obj.BooleanObject)
			if boolResult.Value != tt.expected {
				t.Errorf("Expected %v, got %v", tt.expected, boolResult.Value)
			}
		})
	}
}

func TestCharacterPredicates(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected bool
	}{
		// char-alphabetic? tests
		{"char-alphabetic? with letter", "(char-alphabetic? #\\a)", true},
		{"char-alphabetic? with digit", "(char-alphabetic? #\\5)", false},
		{"char-alphabetic? with space", "(char-alphabetic? #\\ )", false},

		// char-numeric? tests
		{"char-numeric? with digit", "(char-numeric? #\\5)", true},
		{"char-numeric? with letter", "(char-numeric? #\\a)", false},
		{"char-numeric? with space", "(char-numeric? #\\ )", false},

		// char-whitespace? tests
		{"char-whitespace? with space", "(char-whitespace? #\\ )", true},
		{"char-whitespace? with tab", "(char-whitespace? #\\tab)", true},
		{"char-whitespace? with newline", "(char-whitespace? #\\newline)", true},
		{"char-whitespace? with letter", "(char-whitespace? #\\a)", false},

		// char-upper-case? tests
		{"char-upper-case? with uppercase", "(char-upper-case? #\\A)", true},
		{"char-upper-case? with lowercase", "(char-upper-case? #\\a)", false},
		{"char-upper-case? with digit", "(char-upper-case? #\\5)", false},

		// char-lower-case? tests
		{"char-lower-case? with lowercase", "(char-lower-case? #\\a)", true},
		{"char-lower-case? with uppercase", "(char-lower-case? #\\A)", false},
		{"char-lower-case? with digit", "(char-lower-case? #\\5)", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Type() != obj.BOOLEAN_OBJ {
				t.Fatalf("Expected BOOLEAN_OBJ, got %v", result.Type())
			}

			boolResult := result.(*obj.BooleanObject)
			if boolResult.Value != tt.expected {
				t.Errorf("Expected %v, got %v", tt.expected, boolResult.Value)
			}
		})
	}
}

func TestCharacterConversion(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected interface{} // can be int64 for integers or rune for characters
	}{
		// char->integer tests
		{"char->integer with 'a'", "(char->integer #\\a)", int64('a')},
		{"char->integer with 'A'", "(char->integer #\\A)", int64('A')},
		{"char->integer with space", "(char->integer #\\ )", int64(' ')},

		// integer->char tests
		{"integer->char with 97", "(integer->char 97)", rune('a')},
		{"integer->char with 65", "(integer->char 65)", rune('A')},
		{"integer->char with 32", "(integer->char 32)", rune(' ')},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			switch expected := tt.expected.(type) {
			case int64:
				if result.Type() != obj.NUMBER_OBJ {
					t.Fatalf("Expected NUMBER_OBJ, got %v", result.Type())
				}
				numResult := result.(*obj.NumberObject)
				if numResult.Value.Type() != num.INTEGER_TYPE {
					t.Fatalf("Expected INTEGER_TYPE, got %v", numResult.Value.Type())
				}
				intResult := numResult.Value.(num.Integer)
				if intResult.Value != expected {
					t.Errorf("Expected %v, got %v", expected, intResult.Value)
				}
			case rune:
				if result.Type() != obj.CHARACTER_OBJ {
					t.Fatalf("Expected CHARACTER_OBJ, got %v", result.Type())
				}
				charResult := result.(*obj.CharacterObject)
				if charResult.Value != expected {
					t.Errorf("Expected %v, got %v", expected, charResult.Value)
				}
			}
		})
	}
}

func TestCharacterCaseConversion(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected rune
	}{
		{"char-upcase with lowercase", "(char-upcase #\\a)", 'A'},
		{"char-upcase with uppercase", "(char-upcase #\\A)", 'A'},
		{"char-upcase with digit", "(char-upcase #\\5)", '5'},

		{"char-downcase with uppercase", "(char-downcase #\\A)", 'a'},
		{"char-downcase with lowercase", "(char-downcase #\\a)", 'a'},
		{"char-downcase with digit", "(char-downcase #\\5)", '5'},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Type() != obj.CHARACTER_OBJ {
				t.Fatalf("Expected CHARACTER_OBJ, got %v", result.Type())
			}

			charResult := result.(*obj.CharacterObject)
			if charResult.Value != tt.expected {
				t.Errorf("Expected %v, got %v", tt.expected, charResult.Value)
			}
		})
	}
}

func TestStringFunctions(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		// Test make-string
		{"make-string with length only", "(make-string 5)", "\"     \""},
		{"make-string with length and char", "(make-string 3 #\\a)", "\"aaa\""},

		// Test string
		{"string from characters", "(string #\\h #\\e #\\l #\\l #\\o)", "\"hello\""},

		// Test string-length
		{"string-length", "(string-length \"hello\")", "5"},
		{"string-length empty", "(string-length \"\")", "0"},

		// Test string-ref
		{"string-ref", "(string-ref \"hello\" 1)", "#\\e"},
		{"string-ref last char", "(string-ref \"hello\" 4)", "#\\o"},

		// Test string=?
		{"string=? equal", "(string=? \"hello\" \"hello\")", "#t"},
		{"string=? not equal", "(string=? \"hello\" \"world\")", "#f"},

		// Test string-ci=?
		{"string-ci=? equal", "(string-ci=? \"Hello\" \"HELLO\")", "#t"},
		{"string-ci=? not equal", "(string-ci=? \"Hello\" \"world\")", "#f"},

		// Test string<?
		{"string<? true", "(string<? \"abc\" \"def\")", "#t"},
		{"string<? false", "(string<? \"def\" \"abc\")", "#f"},

		// Test string>?
		{"string>? true", "(string>? \"def\" \"abc\")", "#t"},
		{"string>? false", "(string>? \"abc\" \"def\")", "#f"},

		// Test string<=?
		{"string<=? less", "(string<=? \"abc\" \"def\")", "#t"},
		{"string<=? equal", "(string<=? \"abc\" \"abc\")", "#t"},
		{"string<=? false", "(string<=? \"def\" \"abc\")", "#f"},

		// Test string>=?
		{"string>=? greater", "(string>=? \"def\" \"abc\")", "#t"},
		{"string>=? equal", "(string>=? \"abc\" \"abc\")", "#t"},
		{"string>=? false", "(string>=? \"abc\" \"def\")", "#f"},

		// Test string-ci<?
		{"string-ci<? true", "(string-ci<? \"ABC\" \"def\")", "#t"},
		{"string-ci<? false", "(string-ci<? \"DEF\" \"abc\")", "#f"},

		// Test string-ci>?
		{"string-ci>? true", "(string-ci>? \"DEF\" \"abc\")", "#t"},
		{"string-ci>? false", "(string-ci>? \"ABC\" \"def\")", "#f"},

		// Test string-ci<=?
		{"string-ci<=? less", "(string-ci<=? \"ABC\" \"def\")", "#t"},
		{"string-ci<=? equal", "(string-ci<=? \"ABC\" \"abc\")", "#t"},
		{"string-ci<=? false", "(string-ci<=? \"DEF\" \"abc\")", "#f"},

		// Test string-ci>=?
		{"string-ci>=? greater", "(string-ci>=? \"DEF\" \"abc\")", "#t"},
		{"string-ci>=? equal", "(string-ci>=? \"ABC\" \"abc\")", "#t"},
		{"string-ci>=? false", "(string-ci>=? \"ABC\" \"def\")", "#f"},

		// Test substring
		{"substring", "(substring \"hello\" 1 4)", "\"ell\""},
		{"substring to end", "(substring \"hello\" 2 5)", "\"llo\""},

		// Test string-append
		{"string-append", "(string-append \"hello\" \" \" \"world\")", "\"hello world\""},
		{"string-append empty", "(string-append)", "\"\""},

		// Test string->list and list->string are handled separately with proper object comparison
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}

/*
	func TestStringListConversion(t *testing.T) {
		env := obj.NewEnvironment()
		RegisterBuiltinProcedures(env)

		// Test string->list with empty string
		t.Run("string->list empty", func(t *testing.T) {
			input := "(string->list \"\")"
			lexer := lex.NewLexer(input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("parser error: %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("eval error: %v", err)
			}

			if result.Type() != obj.PAIR_OBJ {
				t.Fatalf("Expected pair object, got %v", result.Type())
			}

			pairObj := result.(*obj.PairObject)
			if pairObj.Value != nil {
				t.Errorf("Expected empty list (nil), got %v", pairObj.Value)
			}
		})

		// Test list->string with empty list
		t.Run("list->string empty", func(t *testing.T) {
			// Create an empty list manually
			input := "(list->string ())"
			lexer := lex.NewLexer(input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("parser error: %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("eval error: %v", err)
			}

			if result.Type() != obj.STRING_OBJ {
				t.Fatalf("Expected string object, got %v", result.Type())
			}

			strObj := result.(*obj.StringObject)
			if strObj.Value != "" {
				t.Errorf("Expected empty string, got '%s'", strObj.Value)
			}
		})
	}

	func TestStringFunctionErrors(t *testing.T) {
		env := obj.NewEnvironment()
		RegisterBuiltinProcedures(env)

		errorTests := []struct {
			name        string
			input       string
			expectedErr string
		}{
			// Test make-string errors
			{"make-string wrong arg count", "(make-string)", "make-string requires 1 or 2 arguments"},
			{"make-string non-number", "(make-string #\\a)", "first argument to make-string must be a number"},
			{"make-string non-integer", "(make-string 3.5)", "first argument to make-string must be an integer"},
			{"make-string negative", "(make-string -1)", "first argument to make-string must be non-negative"},
			{"make-string non-char second arg", "(make-string 3 42)", "second argument to make-string must be a character"},

			// Test string errors
			{"string non-char arg", "(string #\\a 42)", "all arguments to string must be characters"},

			// Test string-length errors
			{"string-length wrong arg count", "(string-length)", "string-length requires exactly 1 argument"},
			{"string-length wrong arg count 2", "(string-length \"hello\" \"world\")", "string-length requires exactly 1 argument"},
			{"string-length non-string", "(string-length 42)", "argument to string-length must be a string"},

			// Test string-ref errors
			{"string-ref wrong arg count", "(string-ref)", "string-ref requires exactly 2 arguments"},
			{"string-ref wrong arg count 3", "(string-ref \"hello\" 1 2)", "string-ref requires exactly 2 arguments"},
			{"string-ref non-string", "(string-ref 42 1)", "first argument to string-ref must be a string"},
			{"string-ref non-number", "(string-ref \"hello\" #\\a)", "second argument to string-ref must be a number"},
			{"string-ref non-integer", "(string-ref \"hello\" 1.5)", "second argument to string-ref must be an integer"},
			{"string-ref negative index", "(string-ref \"hello\" -1)", "second argument to string-ref must be non-negative"},
			{"string-ref out of bounds", "(string-ref \"hello\" 10)", "index out of bounds"},

			// Test string-set! errors
			{"string-set! wrong arg count", "(string-set!)", "string-set! requires exactly 3 arguments"},
			{"string-set! wrong arg count 2", "(string-set! \"hello\" 1)", "string-set! requires exactly 3 arguments"},
			{"string-set! non-string", "(string-set! 42 1 #\\x)", "first argument to string-set! must be a string"},
			{"string-set! non-number", "(string-set! \"hello\" #\\a #\\x)", "second argument to string-set! must be a number"},
			{"string-set! non-char", "(string-set! \"hello\" 1 42)", "third argument to string-set! must be a character"},

			// Test string=? errors
			{"string=? wrong arg count", "(string=?)", "string=? requires exactly 2 arguments"},
			{"string=? wrong arg count 1", "(string=? \"hello\")", "string=? requires exactly 2 arguments"},
			{"string=? wrong arg count 3", "(string=? \"hello\" \"world\" \"test\")", "string=? requires exactly 2 arguments"},
			{"string=? non-string first", "(string=? 42 \"world\")", "arguments to string=? must be strings"},
			{"string=? non-string second", "(string=? \"hello\" 42)", "arguments to string=? must be strings"},

			// Test substring errors
			{"substring wrong arg count", "(substring)", "substring requires exactly 3 arguments"},
			{"substring wrong arg count 2", "(substring \"hello\" 1)", "substring requires exactly 3 arguments"},
			{"substring non-string", "(substring 42 1 3)", "first argument to substring must be a string"},
			{"substring non-number start", "(substring \"hello\" #\\a 3)", "second and third arguments to substring must be numbers"},
			{"substring non-number end", "(substring \"hello\" 1 #\\a)", "second and third arguments to substring must be numbers"},
			{"substring non-integer start", "(substring \"hello\" 1.5 3)", "second argument to substring must be an integer"},
			{"substring non-integer end", "(substring \"hello\" 1 3.5)", "third argument to substring must be an integer"},
			{"substring negative start", "(substring \"hello\" -1 3)", "start and end indices must be non-negative"},
			{"substring negative end", "(substring \"hello\" 1 -3)", "start and end indices must be non-negative"},
			{"substring start > end", "(substring \"hello\" 3 1)", "start index must not be greater than end index"},
			{"substring end out of bounds", "(substring \"hello\" 1 10)", "end index out of bounds"},

			// Test string-append errors
			{"string-append non-string", "(string-append \"hello\" 42)", "all arguments to string-append must be strings"},

			// Test string->list errors
			{"string->list wrong arg count", "(string->list)", "string->list requires exactly 1 argument"},
			{"string->list wrong arg count 2", "(string->list \"hello\" \"world\")", "string->list requires exactly 1 argument"},
			{"string->list non-string", "(string->list 42)", "argument to string->list must be a string"},

			// Test list->string errors
			{"list->string wrong arg count", "(list->string)", "list->string requires exactly 1 argument"},
			{"list->string wrong arg count 2", "(list->string () ())", "list->string requires exactly 1 argument"},
			{"list->string non-list", "(list->string 42)", "argument to list->string must be a list"},
			{"list->string non-char elements", "(list->string (list #\\h 42))", "all elements of list must be characters"},
		}

		for _, tt := range errorTests {
			t.Run(tt.name, func(t *testing.T) {
				lexer := lex.NewLexer(tt.input)
				tokens := lexer.ReadTokens()
				parser := parse.NewParser(tokens)
				expr, err := parser.ParseExpression()
				if err != nil {
					t.Fatalf("ParseExpression() error = %v", err)
				}

				result, err := EvalExpression(expr, env)
				if err != nil {
					// Error occurred during evaluation, check if it matches expected error
					if err.Error() != tt.expectedErr {
						t.Errorf("EvalExpression() error = %v, want %v", err.Error(), tt.expectedErr)
					}
					return
				}

				// No error during evaluation, check if result is an error object
				if result.Type() != obj.ERROR_OBJ {
					t.Fatalf("Expected error object, got %v", result.Type())
				}

				errorObj := result.(*Error)
				if errorObj.Message != tt.expectedErr {
					t.Errorf("EvalExpression() error = %v, want %v", errorObj.Message, tt.expectedErr)
				}
			})
		}
	}
*/
func TestCondExpression(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "simple cond with true test",
			input:    "(cond (#t 1) (#f 2))",
			expected: "1",
		},
		{
			name:     "simple cond with false then true test",
			input:    "(cond (#f 1) (#t 2))",
			expected: "2",
		},
		{
			name:     "cond with else clause",
			input:    "(cond (#f 1) (else 3))",
			expected: "3",
		},
		{
			name:     "cond with multiple expressions in body",
			input:    "(cond (#t 1 2 3))",
			expected: "3",
		},
		{
			name:     "cond with comparison test",
			input:    "(cond ((> 2 1) 'greater) ((< 2 1) 'less))",
			expected: "greater",
		},
		{
			name:     "cond with no matching clauses",
			input:    "(cond (#f 1) (#f 2))",
			expected: "",
		},
		{
			name:     "cond with complex expressions",
			input:    "(cond ((= 1 2) (+ 1 2)) ((= 2 2) (+ 3 4)))",
			expected: "7",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()
			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("EvalExpression() = %v, want %v", result.Inspect(), tt.expected)
			}
		})
	}
}
